
#include "linux_def.h"
#include "core.h"
#include "channel.h"
#include "event.h"

int write_channel(int s, channel_t *ch, size_t size)
{
	size_t	n;
	
	struct iovec iov[1];
	struct msghdr msg;

	union{
		struct cmsghdr	cm;
		char	space[CMSG_SPACE(sizeof(int))];	
	}cmsg;	

	if (ch->fd == -1){
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
	}else{
		msg.msg_control = (caddr_t)&cmsg;
		msg.msg_controllen = sizeof(cmsg);

		cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
		cmsg.cm.cmsg_level = SOL_SOCKET;
		cmsg.cm.cmsg_type = SCM_RIGHTS;
		*(int*)CMSG_DATA(&cmsg.cm) = ch->fd;
	}
		
	msg.msg_flags = 0;

	iov[0].iov_base = (char*)ch;
	iov[0].iov_len = size;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	n = sendmsg(s, &msg, 0);

	if (n == -1){
		if (errno == EAGAIN)
			return EAGAIN;

		return errno;
	}

	if (verbose)
		printf("%d Write_channel , ch->fd = %d\n", getpid(), ch->fd);

	return 0;
}

int read_channel(int s, channel_t *ch, size_t size)
{
	ssize_t	n;
	struct iovec iov[1];
	struct msghdr msg;

	union{
		struct cmsghdr cm;
		char	space[CMSG_SPACE(sizeof(int))];
	}cmsg;

	iov[0].iov_base = (char*)ch;
	iov[0].iov_len = size;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	msg.msg_control = (caddr_t)&cmsg;
	msg.msg_controllen = sizeof(cmsg);

	n = recvmsg(s, &msg, 0);

	if (n == -1){
		if (errno == EAGAIN)
			return EAGAIN;

		return errno;
	}
	
	if (n == 0)
		return errno;

	if ((size_t)n < sizeof(channel_t))
		return errno;

	if (ch->command == CMD_OPEN_CHANNEL){
		if (cmsg.cm.cmsg_len < (socklen_t)CMSG_LEN(sizeof(int))){
			return errno;
		}

		if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS){
			return errno;
		}

		ch->fd = *(int*)CMSG_DATA(&cmsg.cm);
	}


	if (verbose)
		printf("%d Read_channel , ch->fd = %d\n", getpid(), ch->fd);

	return n;
}

//添加事件
int add_channel_event(cycle_t *cycle, int fd, int event, event_handler_pt handler)
{
	event_t	*ev, *rev, *wev;	
	connection_t	*c;
		
	c = get_connection(fd);

	if (c == NULL){
		fprintf(stderr, "%s, %d, %s, failed\n", __FILE__, __LINE__, __func__);
		return -1;
	}

	c->pool = cycle->pool;

	rev = c->read;
	wev = c->write;	
	
	ev = (event==READ_EVENT) ? rev : wev;

	ev->handler = handler;

	if (event_flags & USE_EPOLL_EVENT == 0){
		if (epoll_add_connection(c) == -1){
			free_connection(c);	
			return -1;
		}
	}else{
		if(epoll_add_event(ev, event, 0) == E_FAIL){
			free_connection(c);	
			return -1;
		}
	}

//	if (verbose)
//		printf("%s == %d, %s\n", __FILE__, __LINE__, __func__);

	return 0;
}

void close_channel(int *fd)
{
	if (fd[0] != -1)
		if (close(fd[0] == -1))
			fprintf(stderr, "close() channel failed.\n");
	if (fd[1] != -1)
		if (close(fd[1] == -1))
			fprintf(stderr, "close() channel failed.\n");
}

